/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.binding.guicomponents.action;

import java.lang.ref.*;
import java.util.*;
import javax.swing.*;

/**
 * Base implementation of {@link ISelectionAction} storing the passed selection in a memoryleak avoiding way.
 * 
 * @param <T> The type of the selection
 * 
 * @author M. Hautle
 */
public abstract class AbstractSelectionAction<T> extends AbstractAction implements ISelectionAction<T> {
	/** The current selection. */
	protected final SelectionHolder<T> selection = new SelectionHolder<T>();

	/**
	 * Default constructor.
	 */
	public AbstractSelectionAction() {
	}

	/**
	 * Default constructor.
	 * 
	 * @param name The name of the action
	 * @param icon The icon of the action
	 */
	public AbstractSelectionAction(String name, Icon icon) {
		super(name, icon);
	}

	/**
	 * Default constructor.
	 * 
	 * @param name The name of the action
	 */
	public AbstractSelectionAction(String name) {
		super(name);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getActionID() {
		return getClass().getName();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final boolean selectionChanged(List<T> selection) {
		this.selection.set(selection);
		return showActionForSelection(selection);
	}

	/**
	 * Method deciding if this action should be shown/activated for the passed selection.
	 * 
	 * @param selection The selection (non null)
	 * @return True if the action should be shown for the given selection
	 */
	protected boolean showActionForSelection(List<T> selection) {
		return true;
	}

	/**
	 * Returns the current selection.
	 * 
	 * @return The selection
	 */
	protected List<T> getSelection() {
		return selection.get();
	}

	/**
	 * Helper holding the selection of the action but without any strong reference.
	 * 
	 * @param <T> The selection content type
	 * 
	 * @author M. Hautle
	 */
	protected static class SelectionHolder<T> {
		/** A reference on the list itself. (just for performance reasons) */
		private Reference<List<T>> list;

		/** List holding references to each entry of {@link #list}. */
		private List<Reference<T>> elements;

		/**
		 * Sets the list.
		 * 
		 * @param list The list
		 */
		public void set(List<T> list) {
			this.list = new SoftReference<List<T>>(list);
			setElements(list);
		}

		/**
		 * Populates {@link #elements} with the entries of the given list.
		 * 
		 * @param list The list
		 */
		private void setElements(List<T> list) {
			if (list.size() == 0) {
				clear();
				return;
			}
			elements = new ArrayList<Reference<T>>(list.size());
			for (T o : list)
				elements.add(new SoftReference<T>(o));
		}

		/**
		 * Returns the list.<br>
		 * This method returns a list holding all existing entries of the initally passed list.
		 * 
		 * @return The list
		 */
		public List<T> get() {
			if (list == null)
				return Collections.emptyList();
			final List<T> l = list.get();
			if (l != null)
				return l;
			return getList();
		}

		/**
		 * This method creates a new list holding all existing entries of {@link #elements} and sets the reference {@link #list} to the returned list.
		 * 
		 * @return The list
		 */
		private List<T> getList() {
			final List<T> res = new ArrayList<T>();
			for (int i = 0; i < elements.size(); i++) {
				final T el = elements.get(i).get();
				if (el != null)
					res.add(el);
				else
					elements.remove(i);
			}
			// cache the list holding the surviving entries or clear the holder if there are no entries left
			if (res.size() > 0)
				list = new SoftReference<List<T>>(res);
			else
				clear();
			return res;
		}

		/**
		 * Clears the content.
		 */
		public void clear() {
			if (list == null)
				return;
			list = null;
			elements.clear();
			elements = null;
		}
	}
}
